﻿#region using statements

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

#endregion

namespace XNAGameEngine.Particles
{
    /// <summary>
    /// A particle emitter. Emits a single type of particle
    /// </summary>
    public abstract class ParticleEmitter : DrawableGameComponent
    {
        #region local fields

        /// <summary>
        /// list of active particles
        /// </summary>
        protected readonly List<Particle> _particlesList;

        /// <summary>
        /// list of particles waiting to be removed from the particle list
        /// </summary>
        protected readonly List<Particle> _deadParticleList;

        /// <summary>
        /// spritebatch used to render the particles
        /// </summary>
        protected readonly SpriteBatch _spriteBatch;

        /// <summary>
        /// location of this emitter in the world
        /// </summary>
        public Vector2 _location;

        /// <summary>
        /// list of images used for the particles. one is randomly selected
        /// each time a new particle is created
        /// </summary>
        protected readonly List<Texture2D> _texturesList;

        /// <summary>
        /// accumulates time to normalize particle emission rate
        /// </summary>
        protected double _delayTimer;

        /// <summary>
        /// number of particles emitted per second
        /// </summary>
        protected uint _particlesEmittedPerSecond;

        /// <summary>
        /// automatically calculated time between emits based on _delayTimer
        /// and _particlesEmitterPerSecond
        /// </summary>
        protected double _timeBetweenEmits;

        /// <summary>
        /// is this emitter currently emitting
        /// </summary>
        protected bool _isActive;

        //these values are used to initialize new particles
        public Texture2D _newParticleTexture;
        public Color _newParticleColor;
        public double _newParticleTimeToLive;
        public Vector2 _newParticlePosition;
        public Vector2 _newParticlePosVelocity;
        public Vector2 _newParticlePosAcceleration;
        public double _newParticleAngle;
        public double _newParticleAngVelocity;
        public double _newParticleAngAcceleration;
        public Vector2 _newParticleScale;
        public Vector2 _newParticleScaVelocity;
        public Vector2 _newParticleScaAcceleration;

        #endregion

        #region static fields

        /// <summary>
        /// random number generator
        /// </summary>
        protected static readonly Random _random = new Random();

        #endregion

        #region properties

        /// <summary>
        /// Get or set the active state of this emitter
        /// </summary>
        public bool _IsActive
        {
            get { return _isActive; }
            set { _isActive = value; }
        }

        /// <summary>
        /// Get or set the number of particles emitted per second
        /// </summary>
        public uint _ParticlesEmittedPerSecond
        {
            get { return _particlesEmittedPerSecond; }
            set
            {
                _particlesEmittedPerSecond = value;
                _timeBetweenEmits = 1 / ((double)_particlesEmittedPerSecond);
            }
        }

        #endregion

        /// <summary>
        /// create a new particle emitter 
        /// </summary>
        /// <param name="game">reference to the game</param>
        /// <param name="spriteBatch">spritebatch used to draw particles</param>
        /// <param name="location">starting location of the emitter</param>
        public ParticleEmitter(Game game, SpriteBatch spriteBatch,
            Vector2 location)
            : base(game)
        {
            _spriteBatch = spriteBatch;
            _location = location;
            _texturesList = new List<Texture2D>();
            _particlesList = new List<Particle>();
            _deadParticleList = new List<Particle>();
            _delayTimer = 0;
            _isActive = true;
            _ParticlesEmittedPerSecond = 100;
        }



        /// <summary>
        /// Update this emitter and all particles it has emitted
        /// </summary>
        /// <param name="gameTime">snapshot of timing values</param>
        public virtual void Update(GameTime gameTime)
        {
            _delayTimer += (double)gameTime.ElapsedGameTime.Milliseconds / 1000;

            if (_isActive)
            {
                for (; _delayTimer > _timeBetweenEmits;
                    _delayTimer -= _timeBetweenEmits)
                    _particlesList.Add(GenerateNewParticle());
            }

            foreach (Particle particle in _particlesList)
            {
                particle.Update(gameTime);
                if (particle._timeToLive < 0)
                    _deadParticleList.Add(particle);
            }

            foreach (Particle particle in _deadParticleList)
                _particlesList.Remove(particle);

            _deadParticleList.Clear();
        }

        /// <summary>
        /// Draw the particles
        /// </summary>
        /// <param name="gameTime">snapshot of timing values</param>
        public virtual void Draw(GameTime gameTime)
        {
            _spriteBatch.Begin();

            foreach (Particle particle in _particlesList)
                particle.Draw(_spriteBatch);

            _spriteBatch.End();
        }

        /// <summary>
        /// Create a new particle to attach to this emitter
        /// </summary>
        /// <returns></returns>
        protected virtual Particle GenerateNewParticle()
        {
            _newParticleTexture =
                _texturesList[_random.Next(_texturesList.Count)];

            _newParticleColor = new Color((float)_random.NextDouble(),
                (float)_random.NextDouble(), (float)_random.NextDouble());

            _newParticleTimeToLive = 1 + _random.Next(5);

            _newParticlePosition = _location + new Vector2(
                    (float)_random.NextDouble(), (float)_random.NextDouble());

            _newParticlePosVelocity = new Vector2(
                    1f * (float)(_random.NextDouble() * 2 - 1),
                    1f * (float)(_random.NextDouble() * 2 - 1));

            _newParticlePosAcceleration = new Vector2(
                    1f * (float)(_random.NextDouble() * 2 - 1),
                    1f * (float)(_random.NextDouble() * 2 - 1));

            _newParticleAngle = 0;

            _newParticleAngVelocity = 0.1f * (_random.NextDouble() * 2 - 1);

            _newParticleAngAcceleration = 0.1f * (_random.NextDouble() * 2 - 1);

            _newParticleScale = new Vector2(
                    0.1f * (float)(_random.NextDouble() * 2 - 1),
                    0.1f * (float)(_random.NextDouble() * 2 - 1));

            _newParticleScaVelocity = new Vector2(
                    0.1f * (float)(_random.NextDouble() * 2 - 1),
                    0.1f * (float)(_random.NextDouble() * 2 - 1));

            _newParticleScaAcceleration = new Vector2(
                    0.1f * (float)(_random.NextDouble() * 2 - 1),
                    0.1f * (float)(_random.NextDouble() * 2 - 1));

            return new Particle(
                _newParticleTexture, _newParticleColor,
                _newParticleTimeToLive, _newParticlePosition,
                _newParticlePosVelocity, _newParticlePosAcceleration,
                _newParticleAngle, _newParticleAngVelocity,
                _newParticleAngAcceleration, _newParticleScale,
                _newParticleScaVelocity, _newParticleScaAcceleration);
        }
    }
}